import {
  AmbientLight,
  AxesHelper,
  BoxGeometry,
  CubeTextureLoader,
  DirectionalLight,
  GridHelper,
  Group,
  Mesh,
  MeshStandardMaterial,
  PerspectiveCamera,
  Raycaster,
  Scene,
  Vector2,
  WebGLRenderer,
} from "three";
import { GLTFLoader, OrbitControls } from "three/examples/jsm/Addons.js";

export default class Y3DEngine {
  constructor(options) {
    this.options = options;
    this.init();
    this.animate();
  }

  init() {
    this.initScene();
    this.initCamera();
    this.initRenderer();
    this.initLight();
    this.initObject();
    this.initControls();
    this.initEvent();
    this.initHelper();
    this.initMaterial();
  }

  initMaterial() {
    this.materials = {
      桥台: new MeshStandardMaterial({
        color: 0xa0a0a0,
        metalness: 0.05,
        roughness: 0.8,
      }),
      支座: new MeshStandardMaterial({
        color: 0x666699,
        metalness: 0.3,
        roughness: 0.8,
      }),
      墩台基础: new MeshStandardMaterial({
        color: 0xbbbbbb,
        metalness: 0.05,
        roughness: 0.8,
      }),
      伸缩缝装置: new MeshStandardMaterial({
        color: 0xcccccc,
        metalness: 0.25,
        roughness: 0.8,
      }),
      桥墩: new MeshStandardMaterial({
        color: 0xcccccc,
        metalness: 0.05,
        roughness: 0.8,
      }),
      上部承重构件: new MeshStandardMaterial({
        color: 0xcccccc,
        metalness: 0.1,
        roughness: 0.8,
      }),
      桥面铺装: new MeshStandardMaterial({
        color: 0x333333,
        metalness: 0.1,
        roughness: 0.8,
      }),
      "栏杆、护栏": new MeshStandardMaterial({
        color: 0xffffff,
        metalness: 0.05,
        roughness: 0.8,
      }),
      上部一般构件: new MeshStandardMaterial({
        color: 0xdddddd,
        metalness: 0.12,
        roughness: 0.7,
      }),
    };
  }

  initHelper() {
    this.helpers = new Group();
    this.scene.add(this.helpers);
    const axesHelper = new AxesHelper(5);
    this.helpers.add(axesHelper);

    const gridHelper = new GridHelper(10, 2);
    this.helpers.add(gridHelper);
  }

  animate = () => {
    requestAnimationFrame(this.animate);
    this.renderer.render(this.scene, this.camera);
  };

  initEvent() {
    window.addEventListener("resize", this.onWindowResize);
    window.addEventListener("pointerup", this.onPointerUp);
  }

  onPointerUp = (e) => {
    // debugger
    if (e.ctrlKey) {
      const x = (e.clientX / window.innerWidth) * 2 - 1;
      const y = (-e.clientY / window.innerHeight) * 2 + 1;
      this.pointer = new Vector2(x, y);
      this.raycaster = new Raycaster();
      this.raycaster.setFromCamera(this.pointer, this.camera);
      const intersects = this.raycaster.intersectObjects(
        this.scene.children,
        true
      );
      if (intersects.length > 0) {
        const object = intersects[0].object;
        this.seeToObj(object.name);
      }
    }
  };

  onWindowResize = () => {
    this.camera.aspect = window.innerWidth / window.innerHeight;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(window.innerWidth, window.innerHeight);
  };

  initControls() {
    this.controls = new OrbitControls(this.camera, this.renderer.domElement);
  }

  initObject() {
    const geometry = new BoxGeometry(1, 1, 1);
    const material = new MeshStandardMaterial({
      color: 0x00ff00,
      metalness: 0.1,
      roughness: 0.9,
    });
    this.cube = new Mesh(geometry, material);
    this.scene.add(this.cube);
  }

  initLight() {
    this.lights = new Group();
    this.scene.add(this.lights);

    const light = new AmbientLight(0xffffff, 0.3);
    this.lights.add(light);

    const light2 = new DirectionalLight(0xffffff, 0.8);
    light2.position.set(1, 1, 0);
    this.lights.add(light2);

    const light3 = new DirectionalLight(0xffffff, 0.5);
    light3.position.set(-1, 1, 0);
    this.lights.add(light3);

    const light4 = new DirectionalLight(0xffffff, 0.2);
    light4.position.set(0, -1, 0);
    this.lights.add(light4);

    const url = "px180.png";
    const cubeTexture = new CubeTextureLoader()
      .setPath("../assets/")
      .load([url, url, url, url, url, url]);
    this.scene.environment = cubeTexture;
    this.scene.environmentIntensity = 0.5;
  }

  initRenderer() {
    this.renderer = new WebGLRenderer({
      antialias: true,
    });
    this.renderer.setClearColor(0x87ceeb);
    this.renderer.setSize(window.innerWidth, window.innerHeight);
    this.renderer.setPixelRatio(window.devicePixelRatio);
    document.body.appendChild(this.renderer.domElement);
  }

  initScene() {
    this.scene = new Scene();
  }

  initCamera() {
    this.camera = new PerspectiveCamera(
      45,
      window.innerWidth / window.innerHeight,
      1,
      10000
    );
    this.camera.position.set(50, 0, 50);
  }

  loadGLTF = async (url) => {
    const loader = new GLTFLoader();
    const gltf = await loader.loadAsync(url);
    this.scene.add(gltf.scene);
    gltf.scene.children.forEach((item) => {
      if (item.isMesh && item.name) {
        const site = item.name.split("$")[0];
        const [part, region, component] = site.split(",");
        Object.assign(item.userData, { site, part, region, component });
        if (item.material.name == "") {
          if (this.materials[site]) {
            item.material = this.materials[site];
            item.material.name = site;
          } else if (this.materials[part]) {
            item.material = this.materials[part];
            item.material.name = part;
          } else if (this.materials[region]) {
            item.material = this.materials[region];
            item.material.name = region;
          }
        }
      }
    });
    const names = gltf.scene.children.map((item) => item.name);
    console.log(names);
    console.log(gltf);
    window.gltf = gltf;
    this.seeToObj(names[Math.floor(names.length / 2)]);
  };

  seeToObj = (name) => {
    const obj = this.scene.getObjectByName(name);
    if (obj) {
      this.seeTo(obj.position);
    }
  };

  seeTo = (position) => {
    const direction = this.camera.position.clone().sub(this.controls.target);
    this.camera.position.copy(direction).add(position);
    this.controls.target.copy(position);
    this.controls.update();
  };
}

window.addEventListener("DOMContentLoaded", () => {
  const y3engine = new Y3DEngine();
  window.y3engine = y3engine;
  y3engine.loadGLTF("../assets/峒河高架桥（上行）.glb");
});
